using System;
using NyMPH.Persistence;

/// <summary>
/// This class holds the actual data that makes a Patch File used by the Editor Software. It also represents a 'version' of that data associated with a single Patch instance.
/// </summary>
public class PatchData : IPersistence, ICloneable, IEquatable<PatchData>, IComparable<PatchData>

{
	private int _OID;
	private string _version;
	private string _data;
	private int _parentOID;
	private string _location;
	private  DateTime _lastModified;
	private DateTime _created;
    
    #region Properties

    /// <summary>
    /// The unique ObjectID within the NyMPH Patch table.
    /// </summary>
    public int OID
    {
        set
        {
            _OID = value;
            this._hasChanges = true;
        }
        get { return _OID; }
    }

    /// <summary>
    /// This is the user-supplied name for this version of Patch Data. For freshly loaded patches, it is set to Original. It must be unique within a Patch.
    /// </summary>
    public string Version
    {
        set
        {
            _version = value;
           SetModified();
        }
        get { return _version; }
    }

    private void SetModified()
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// This is the raw data that makes up a Patch File. 
    /// </summary>
    public string Data
    {
        set
        {
            _data = value;
           SetModified();
        }
        get { return _data; }
    }

    /// <summary>
    /// This is the OID of the Patch object this instance belongs to.
    /// </summary>
    public int ParentOID
    {
        set
        {
            _parentOID = value;
            this._hasChanges = true;
        }
        get { return _parentOID; }
    }

    /// <summary>
    /// The location in the File System of this Patch Data
    /// </summary>
    public string Location
    {//todo: not used, refer to PatchFile instance instead?
        set
        {
            _location = value;
            this._hasChanges = true;
        }
        get { return _location; }
    }

    /// <summary>
    /// The date and time the Patch data was last modified and saved.
    /// </summary>
    public DateTime LastModified
    {
        set
        {
            _lastModified = value;
            this._hasChanges = true;
        }
        get { return _lastModified; }
    }

    /// <summary>
    /// The date and time this Patch was added to the NyMPH System.
    /// </summary>
    public DateTime Created
    {
        set
        {
            _created = value;
            this._hasChanges = true;
        }
        get { return _created; }
    } 

    #endregion

    #region Constructors

    /// <summary>
    /// Default constructor
    /// </summary>
    public PatchData()
    {

    }

    #endregion

    #region Methods

    public PatchFile CreateFile()
    {
        PatchFile _temp = new PatchFile();

        _temp.Data = new PatchData();
        _temp.Data.Data = this.Data;
        return _temp;
    }

    /// <summary>
    /// Create a PatchData instance with data from a PatchFile instance
    /// </summary>
    /// <param name="File">The PatchFile instance to create PatchData from.</param>
    /// <returns>A new instance of PatchData.</returns>
    /// <remarks>TODO: I'm not sure what this was supposed to do; this is a guess.</remarks>
    public static PatchData ReadFile(PatchFile File)
    {
        PatchData _temp = new PatchData();
        throw new NotImplementedException("TODO:");
     //TODO WTF?

    }

    public static bool ComparePatchData(PatchData A, PatchData B)
    {

        //todo: compare the raw data several different ways to determine if they are equal
        // fore instance: compare binary (exact), compare case-insenstive (close), etc.
        throw new NotImplementedException("TODO:");
    }
    #endregion

    #region IPersistence Members

    private bool _hasChanges = false;

    bool IPersistence.Save()
    {
        throw new NotImplementedException();
    }

    bool IPersistence.Load()
    {
        throw new NotImplementedException();
    }

    bool IPersistence.Delete()
    {
        throw new NotImplementedException();
    }

    int IPersistence.GetOID()
    {
        throw new NotImplementedException();
    }

    void IPersistence.Find(Criteria item)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Indicates whether this object has unsaved changes.
    /// </summary>
    /// <returns>True if changes need to be saved, else False.</returns>
    bool IPersistence.HasChanges()
    {
        return _hasChanges;
    }

    /// <summary>
    /// Set a flag indicating this object has changed from its persisted state in the database.
    /// </summary>
    /// <param name="Dirty">True if the object has changes, else False.</param>
    void IPersistence.HasChanges(bool Changed)
    {
        _hasChanges = Changed;
    }

    #endregion

    #region IEquatable<PatchData> Members
    
    /// <summary>
    /// Determines whether this instance of PatchData and a specified object, which must also be a PatchData object, have the same values.
    /// </summary>
    /// <param name="Obj">The PatchData object to compare values with</param>
    /// <returns>True if all properties have matching values, otherwise false.</returns>
    /// <remarks></remarks>
    public bool Equals(PatchData Obj)
    {
        //AND all equality evaluations together
     
        //compare 'easy' ones first to conserve resources; if any of the properties are different it is not equal and we can skip the more costly evaluation of the raw data  
        if
            (this.Version == Obj.Version && this.Location == Obj.Location && this.LastModified == Obj.LastModified && this.Created == Obj.Created )
        {
            return this.Data.Equals(Obj.Data, StringComparison.OrdinalIgnoreCase);         
        }
        else
        {
            return false;
        }
    }

    /// <summary>
    /// Determines whether this instance of PatchData and a specified object, which must also be a PatchData object, have the same values.
    /// </summary>
    /// <param name="Obj">The PatchData to compare values with</param>
    /// <returns>True if all properties have matching values, otherwise false.</returns>
    /// <remarks>Overrides the Object.Equals(object) method. This ensures all invocations of Equals() for the PatchData type return the same result.</remarks>
    public override bool Equals(object Obj)
    {
        //use base class method for comparing nulls; casting them would throw an exception
        if (Obj == null) return base.Equals(Obj);

        //make sure object can be cast to PatchData and call the Equals(PatchData) method
        if (Obj is PatchData)
        {
            return Equals((PatchData)Obj);
        }
        else
        {
            throw new InvalidCastException("The 'Obj' argument is not a PatchData object.");
        }
    }

    /// <summary>
    /// Provides a hash value based on XORing the Name, Version, Location, LastModified, and Created properties together. Two PatchData objects with the same values will produce the same hash code, although it isn't guaranteed to be unique
    /// </summary>
    /// <returns>The hash created from (Name xor Version xor Location xor LastModified xor Created)</returns>
    /// <remarks>MSDN recommends overriding this method when implementing IEquatable. </remarks>
    public override int GetHashCode()
    {
        return  this.Version.GetHashCode() ^ this.Location.GetHashCode() ^ this.LastModified.GetHashCode() ^ this.Created.GetHashCode();
    }

    /// <summary>
    /// Create equality operator for PatchData objects; allows expressions like {if (PatchDataA = PatchDataB)} to compare each member value instead of the object reference
    /// </summary>
    public static bool operator ==(PatchData PatchDataA, PatchData PatchDataB)
    {
        return PatchDataA.Equals(PatchDataB);
    }

    /// <summary>
    /// Create inequality operator for PatchData objects; allows expressions like {if (PatchDataA != PatchDataB)} to compare each member value instead of the object reference
    /// </summary>
    public static bool operator !=(PatchData PatchDataA, PatchData PatchDataB)
    {
        return !PatchDataA.Equals(PatchDataB);
    }

    #endregion

    #region IComparable<PatchData> Members

    /// <summary>
    /// This is required to implement IComparable, which is used when sorting PatchData instances. These are sorted by Name, then Version
    /// </summary>
    /// <param name="other">The PatchData object to compare values with</param>
    /// <returns>Less than 0 if 'other' PatchData should be earlier in the sort order, 0 if equal, and greater than zero if the 'other' PatchData should come after this Core.</returns>
    /// <remarks></remarks>
    public int CompareTo(PatchData other)
    {
        //TODO: Get parent's patch name?
        throw new NotImplementedException("TODO:");
        //compare by name with the Version string appended to it.
        return string.Compare(this.Version,  other.Version, StringComparison.OrdinalIgnoreCase);
    }

    #endregion

    #region ICloneable Members

    public object Clone()
    {
        throw new NotImplementedException();
    }

    #endregion
}
